﻿namespace OnlineVideos.RingToneEditor
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.CompilerServices;

    public class Mp3FileReader : WaveStream
    {
        private int bytesPerSample;
        private long dataStartPosition;
        private byte[] decompressBuffer;
        private int decompressBufferOffset;
        private int decompressLeftovers;
        private IMp3FrameDecompressor decompressor;
        private int frameLengthInBytes;
        private byte[] id3v1Tag;
        private Id3v2Tag id3v2Tag;
        private long mp3DataLength;
        private Stream mp3Stream;
        private bool ownInputStream;
        private object repositionLock;
        private int sampleRate;
        private List<Mp3Index> tableOfContents;
        private int tocIndex;
        private int totalSamples;
        private WaveFormat waveFormat;
        private XingHeader xingHeader;

        public Mp3FileReader(Stream inputStream)
        {
            this.repositionLock = new object();
            this.mp3Stream = inputStream;
            this.id3v2Tag = Id3v2Tag.ReadTag(this.mp3Stream);
            this.dataStartPosition = this.mp3Stream.Position;
            Mp3Frame frame = Mp3Frame.LoadFromStream(this.mp3Stream);
            this.sampleRate = frame.SampleRate;
            this.frameLengthInBytes = frame.FrameLength;
            double bitRate = frame.BitRate;
            this.xingHeader = XingHeader.LoadXingHeader(frame);
            if (this.xingHeader != null)
            {
                this.dataStartPosition = this.mp3Stream.Position;
            }
            this.mp3DataLength = this.mp3Stream.Length - this.dataStartPosition;
            this.mp3Stream.Position = this.mp3Stream.Length - 0x80L;
            byte[] buffer = new byte[0x80];
            this.mp3Stream.Read(buffer, 0, 3);
            if (((buffer[0] == 0x54) && (buffer[1] == 0x41)) && (buffer[2] == 0x47))
            {
                this.id3v1Tag = buffer;
                this.mp3DataLength -= 0x80L;
            }
            this.mp3Stream.Position = this.dataStartPosition;
            this.CreateTableOfContents();
            this.tocIndex = 0;
            bitRate = (this.mp3DataLength * 8.0) / this.TotalSeconds();
            this.mp3Stream.Position = this.dataStartPosition;
            this.Mp3WaveFormat = new Mp3WaveFormat(this.sampleRate, (frame.ChannelMode == ChannelMode.Mono) ? 1 : 2, this.frameLengthInBytes, (int) bitRate);
            this.decompressor = new AcmMp3FrameDecompressor(this.Mp3WaveFormat);
            this.waveFormat = this.decompressor.OutputFormat;
            this.bytesPerSample = (this.decompressor.OutputFormat.BitsPerSample / 8) * this.decompressor.OutputFormat.Channels;
            this.decompressBuffer = new byte[(0x480 * this.bytesPerSample) * 2];
        }

        public Mp3FileReader(string mp3FileName) : this(File.OpenRead(mp3FileName))
        {
            this.ownInputStream = true;
        }

        private void CreateTableOfContents()
        {
            try
            {
                this.tableOfContents = new List<Mp3Index>((int) (this.mp3DataLength / 400L));
                Mp3Frame frame = null;
                this.totalSamples = 0;
                do
                {
                    Mp3Index item = new Mp3Index {
                        FilePosition = this.mp3Stream.Position,
                        SamplePosition = this.totalSamples
                    };
                    frame = this.ReadNextFrame(false);
                    if (frame != null)
                    {
                        this.totalSamples += frame.SampleCount;
                        item.SampleCount = frame.SampleCount;
                        item.ByteCount = (int) (this.mp3Stream.Position - item.FilePosition);
                        this.tableOfContents.Add(item);
                    }
                }
                while (frame != null);
            }
            catch (EndOfStreamException)
            {
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.mp3Stream != null)
                {
                    if (this.ownInputStream)
                    {
                        this.mp3Stream.Dispose();
                    }
                    this.mp3Stream = null;
                }
                if (this.decompressor != null)
                {
                    this.decompressor.Dispose();
                    this.decompressor = null;
                }
            }
            base.Dispose(disposing);
        }

        public override int Read(byte[] sampleBuffer, int offset, int numBytes)
        {
            int num = 0;
            lock (this.repositionLock)
            {
                int num2;
                if (this.decompressLeftovers != 0)
                {
                    num2 = Math.Min(this.decompressLeftovers, numBytes);
                    Array.Copy(this.decompressBuffer, this.decompressBufferOffset, sampleBuffer, offset, num2);
                    this.decompressLeftovers -= num2;
                    if (this.decompressLeftovers == 0)
                    {
                        this.decompressBufferOffset = 0;
                    }
                    else
                    {
                        this.decompressBufferOffset += num2;
                    }
                    num += num2;
                    offset += num2;
                }
                while (num < numBytes)
                {
                    Mp3Frame frame = this.ReadNextFrame();
                    if (frame == null)
                    {
                        goto Label_012A;
                    }
                    int num3 = this.decompressor.DecompressFrame(frame, this.decompressBuffer, 0);
                    num2 = Math.Min(num3, numBytes - num);
                    Array.Copy(this.decompressBuffer, 0, sampleBuffer, offset, num2);
                    if (num2 < num3)
                    {
                        this.decompressBufferOffset = num2;
                        this.decompressLeftovers = num3 - num2;
                    }
                    else
                    {
                        this.decompressBufferOffset = 0;
                        this.decompressLeftovers = 0;
                    }
                    offset += num2;
                    num += num2;
                }
            }
        Label_012A:
            Debug.Assert(num <= numBytes, "MP3 File Reader read too much");
            return num;
        }

        public Mp3Frame ReadNextFrame()
        {
            return this.ReadNextFrame(true);
        }

        private Mp3Frame ReadNextFrame(bool readData)
        {
            Mp3Frame frame = null;
            try
            {
                frame = Mp3Frame.LoadFromStream(this.mp3Stream, readData);
                if (frame != null)
                {
                    this.tocIndex++;
                }
            }
            catch (EndOfStreamException)
            {
            }
            return frame;
        }

        private double TotalSeconds()
        {
            return (((double) this.totalSamples) / ((double) this.sampleRate));
        }

        public byte[] Id3v1Tag
        {
            get
            {
                return this.id3v1Tag;
            }
        }

        public Id3v2Tag Id3v2Tag
        {
            get
            {
                return this.id3v2Tag;
            }
        }

        public override long Length
        {
            get
            {
                return (long) (this.totalSamples * this.bytesPerSample);
            }
        }

        public Mp3WaveFormat Mp3WaveFormat { get; private set; }

        public override long Position
        {
            get
            {
                if (this.tocIndex >= this.tableOfContents.Count)
                {
                    return this.Length;
                }
                return ((this.tableOfContents[this.tocIndex].SamplePosition * this.bytesPerSample) + this.decompressBufferOffset);
            }
            set
            {
                lock (this.repositionLock)
                {
                    value = Math.Max(Math.Min(value, this.Length), 0L);
                    long num = value / ((long) this.bytesPerSample);
                    Mp3Index index = null;
                    for (int i = 0; i < this.tableOfContents.Count; i++)
                    {
                        if (this.tableOfContents[i].SamplePosition >= num)
                        {
                            index = this.tableOfContents[i];
                            this.tocIndex = i;
                            break;
                        }
                    }
                    if (index != null)
                    {
                        this.mp3Stream.Position = index.FilePosition;
                    }
                    else
                    {
                        this.mp3Stream.Position = this.mp3DataLength + this.dataStartPosition;
                    }
                    this.decompressBufferOffset = 0;
                    this.decompressLeftovers = 0;
                }
            }
        }

        public override WaveFormat WaveFormat
        {
            get
            {
                return this.waveFormat;
            }
        }

        public XingHeader XingHeader
        {
            get
            {
                return this.xingHeader;
            }
        }
    }
}

